<?php
#
# dmBridge: a data access framework for CONTENTdm(R)
#
# Copyright © 2009, 2010, 2011 Board of Regents of the Nevada System of Higher
# Education, on behalf of the University of Nevada, Las Vegas
#

/**
 * Encapsulates an HTTP response.
 *
 * @author Alex Dolski <alex.dolski@unlv.edu>
 * @license http://www.opensource.org/licenses/mit-license.php
 */
final class DMHTTPResponse extends DMHTTPMessage {

	/** @var array of key-value pairs */
	private $cookies = array();
	/** @var int */
	private $status_code;
	/** @var string */
	private $status_message;

	/**
	 * @param int code HTTP status code
	 * @return string HTTP status message
	 */
	private static function getMessageForStatusCode($code) {
		$msgs = array(
			100 => 'Continue',
			101 => 'Switching Protocols',
			200 => 'OK',
			201 => 'Created',
			202 => 'Accepted',
			203 => 'Non-Authoritative Information',
			204 => 'No Content',
			205 => 'Reset Content',
			206 => 'Partial Content',
			300 => 'Multiple Choices',
			301 => 'Moved Permanently',
			302 => 'Found',
			303 => 'See Other',
			304 => 'Not Modified',
			305 => 'Use Proxy',
			307 => 'Temporary Redirect',
			400 => 'Bad Request',
			401 => 'Unauthorized',
			402 => 'Payment Required',
			403 => 'Forbidden',
			404 => 'Not Found',
			405 => 'Method Not Allowed',
			406 => 'Not Acceptable',
			407 => 'Proxy Authentication Required',
			408 => 'Request Timeout',
			409 => 'Conflict',
			410 => 'Gone',
			411 => 'Length Required',
			412 => 'Precondition Failed',
			413 => 'Request Entity Too Large',
			414 => 'Request-URI Too Long',
			415 => 'Unsupported Media Type',
			416 => 'Requested Range Not Satisfiable',
			417 => 'Expectation Failed',
			500 => 'Internal Server Error',
			501 => 'Not Implemented',
			502 => 'Bad Gateway',
			503 => 'Service Unavailable',
			504 => 'Gateway Timeout',
			505 => 'HTTP Version Not Supported'
		);
		return array_key_exists($code, $msgs) ? $msgs[$code] : null;
	}

	public function __construct() {
		$this->setHTTPVersion("1.1");
		$this->setStatusCode(200);
	}

	/**
	 * @return string String representation of the body of the response
	 * representation.
	 */
	public function __toString() {
		return (string) $this->getRepresentation()->getBody();
	}

	/**
	 * @param string key
	 * @param string value
	 */
	public function addCookie($key, $value) {
		$this->cookies[$key] = $value;
	}

	/**
	 * @param string key
	 * @return string
	 */
	public function getCookie($key) {
		return array_key_exists($key, $this->cookies)
				? $this->cookies[$key] : null;
	}

	/**
	 * @return array Array of key-value pairs
	 */
	public function getCookies() {
		return $this->cookies;
	}

	/**
	 * Sends the response. If the body is a DMFile, it will be streamed using
	 * readfile(). If it is a DMShellCommand, it will be executed with popen()
	 * and the output streamed via echo. If any other type, it will be echoed.
	 * 
	 * @return void
	 */
	public function send() {
		$code = $this->getStatusCode();
		$message = $this->getStatusMessage()
				? $this->getStatusMessage()
				: self::getMessageForStatusCode($code);
		header(sprintf("HTTP/%s %d %s",
				$this->getHTTPVersion(), $code, $message));

		foreach ($this->getHeaders() as $header) {
			header($header['key'] . ": " . $header['value']);
		}
		header(sprintf("Content-type: %s; charset=%s",
				$this->getRepresentation()->getMediaType(),
				$this->getRepresentation()->getCharacterSet()));

		$body = $this->getRepresentation()->getBody();
		if ($body instanceof DMFile && file_exists($body->getPathname())) {
			readfile($body->getPathname());
		} else if ($body instanceof DMShellCommand) {
			$ph = popen($body->getCommand(), "r");
			while (!feof($ph)) {
				echo fread($ph, 2096);
			}
			pclose($ph);
		} else {
			echo $this->getRepresentation()->getBody();
		}
	}

	/**
	 * @return int
	 */
	public function getStatusCode() {
		return $this->status_code;
	}

	/**
	 * @param int code
	 */
	public function setStatusCode($code) {
		$this->status_code = (int) $code;
	}

	/**
	 * @return string
	 */
	public function getStatusMessage() {
		return $this->status_message;
	}

	/**
	 * @param string message
	 */
	public function setStatusMessage($message) {
		$this->status_message = $message;
	}

}
